#!/usr/local/bin/python3
# *_* coding: UTF-8 *_*
# @IDE: PyCharm
# @Version: Python3.97
# @Author: Kendrick.Kc
# @Email: 509556985@qq.com
# @File: binance.py
# @Inst: 中间件
# @Time: 2023/6/23 13:23
# -----


import django

django.setup()  # 为在子进程中调用django应用，需在子进程启动时初始化django

import basic
import datetime
import time
import traceback

from binance.error import ClientError, ServerError
from binance.um_futures import UMFutures
from utils.message import Messages
from utils.db_cache import DBCache
from basic.models import UsersKeyModel


class Binance(UMFutures, Messages, DBCache):

    def __init__(self, parameter, strategy, exchange=basic.Exchange.binance, pan_opening="1",
                 exchange_url="https://fapi.binance.com"):
        self.parameter = parameter
        self.exchange_name = exchange
        self.strategy_name = strategy
        self.exchange_strategy = f"{exchange}_{strategy}"
        self.pan_openings = {'pan': int(pan_opening), "note": "实盘" if pan_opening == '0' else "模拟盘"}
        res_obj = UsersKeyModel.objects.filter(exchange=exchange, strategy=strategy).first()
        super(UMFutures, self).__init__(key=res_obj.api_key, secret=res_obj.secret_key, base_url=exchange_url)
        Messages.__init__(self, self.exchange_strategy, self.pan_openings["note"])
        DBCache.__init__(self, self.exchange_strategy)

    def rest(self, *args, **kwargs):
        """
        接口
        示例：self.rest('while_info', 'mark_price_klines', symbol='ETHUSDT', interval='1d', limit=1)
        :param args: args[0]：info/while_info ， args[1]：SDK提供的接口
        :param kwargs: 接口参数
        :return: 执行对应函数info/while_info
        """
        if len(args) > 2:
            if isinstance(args[2], list):
                res = getattr(self, args[0])(f"self.{args[1]}", args[2])
            else:
                res = getattr(self, args[0])(f"self.{args[1]}", **kwargs)
        else:
            res = getattr(self, args[0])(f"self.{args[1]}", **kwargs)
        return res

    def info(self, *args, **kwargs):
        """
        接口发起对接
        :param args: 接口
        :param kwargs: 参数
        :return: list 或 bool 或 sCode
        """
        if len(args) > 1:
            _Eval = [args[0], f"({args[1]})"]
        else:
            _Eval = [args[0], f"(**{kwargs})"]
        _Class = ''
        _Class = _Class.join(_Eval)
        try:
            try:
                return eval(_Class)
            except ServerError:
                self.logger_error(f"服务端错误. \n{traceback.format_exc()}")
                return None
        except ClientError as client_error:
            client_error.rest = _Class
            return self.filter_error_code(error=client_error)

    def filter_error_code(self, error):
        """
        过滤错误信息
        :param error: 错误信息
        :return: 程序退出或True或None
        """
        try:
            if str(error.error_code) in ['-4046', '-1001']:
                self.logger_error(f"【状态码】{error.error_code}【原文】{error.error_message}")
            elif str(error.error_code) in ['-1003']:
                banned = float(error.error_message.split('.')[3].split(' ')[-1]) / 1000
                sleep = (datetime.datetime.fromtimestamp(banned) - datetime.datetime.fromtimestamp(time.time())).seconds
                self.logger_warning(f"访问频率过快, 交易所要求等待{sleep}秒后再进行访问接口.【原文】{error.error_message}")
                time.sleep(sleep)
            else:
                self.logger_error(f"【状态码】{error.error_code}【原文】{error.error_message}【接口】{error.rest}")
                return None
        except Exception:
            self.logger_info(f"过滤错误信息异常: \n {traceback.format_exc()}")

    def while_info(self, *args, **kwargs):
        """
        接口循环发起对接
        :param args: 接口
        :param kwargs: 参数，其中必带一个 msg 参数
        :return: list 或 bool
        """
        num = 10
        for i in range(num):
            if i == num:
                self.logger_error(f"while_info {num}次请求失败, 检查与交易所的网络通讯.")
            ex_res = self.info(*args, **kwargs)
            if ex_res:
                return ex_res
            continue

    def position_risk(self, symbol):
        """
        用户持仓风险V2
        :return:
        """
        ex_res = self.rest("while_info", "get_position_risk", symbol=symbol)
        return ex_res

    def get_user_position(self, symbol=None):
        """
        获取用户持仓风险数据
        :return: number:仓位总数量, data:持仓信息数据
        """

        data = self.rest("while_info", "get_position_risk", symbol=symbol)
        position_number = float(0)
        for _ in data:
            position_number += abs(float(_['positionAmt']))
        return {"number": position_number, "data": data}

    def exchange_available_balance(self):
        """ 交易所下单可用余额 """
        balance = self.rest("while_info", "balance")
        for item in balance:
            if item["asset"] == self.parameter.ccy:
                return round(float(item["availableBalance"]), 2)
        return None

    def rpcc_exchange_info(self, symbol):
        """ 获取交易规则 """
        ex_res = self.rest("while_info", "exchange_info")
        data = dict()
        for item in ex_res["symbols"]:
            if item["symbol"] == symbol:
                for fil in item["filters"]:
                    data["this_decimal"] = int(item["quantityPrecision"])  # 下单数量小数点位数
                    data["this_price_decimal"] = int(item["pricePrecision"])  # 价格小数点位数

                    if fil["filterType"] == "LOT_SIZE":  # 数量限制
                        data["this_min_qty"] = float(fil["minQty"])  # 订单最小数量限制

                    if fil["filterType"] == "MIN_NOTIONAL":  # 最小名义价值
                        data["this_min_notional"] = float(fil["notional"])  # 最小下单金额
                break
        return data

    def change_margin(self, symbol, side, amount):
        """ 调整逐仓保证金 """
        self.rest("info", "modify_isolated_position_margin", symbol=symbol, positionSide=side, type=1, amount=amount)
